﻿using PGMHelper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Spectrum
{
    /// <summary>
    /// 响应枚举
    /// </summary>
    public enum SpectrumType
    {
        /// <summary>
        /// 周期
        /// </summary>
        Period = 0,

        /// <summary>
        /// 加速度谱值
        /// </summary>
        Afa = 1,

        /// <summary>
        /// 速度谱值
        /// </summary>
        Velocity = 2,

        /// <summary>
        /// 位移谱值
        /// </summary>
        Disp = 3,
    }

    /// <summary>
    /// 求解反应谱的基本方法
    /// </summary>
    public class SpectrumMethod
    {
        /// <summary>
        /// 获得NewMark参数列表
        /// </summary>
        /// <param name="beta"></param>
        /// <param name="gamma"></param>
        /// <returns></returns>
        public static List<float> GetNewMarkParamList(float beta, float gamma, float dt)
        {
            return new List<float>
            {
            1 / (beta * (float)Math.Pow(dt, 2)),
            gamma / (beta * dt),
            1 / (beta * dt),
            1 / (2 * beta) - 1,
            gamma / beta - 1,
            dt / 2 * (gamma / beta - 2),
            dt * (1 - gamma),
            gamma * dt,
            };
        }

        /// <summary>
        /// 获得反应谱分析结果
        /// </summary>
        /// <param name="originAList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="periodLength"></param>
        /// <param name="deltaPeriod"></param>
        /// <param name="integarteDt"></param>
        /// <param name="duration"></param>
        /// <returns></returns>
        public static List<SpectParam> GetResults(List<AccelParam> originAList, float dampingRatio = 0.05F, float periodLength = 10F,
            float deltaPeriod = 0.02F, float integarteDt = 0.02F, float duration = -1, bool isPeseudo = true)
        {
            //构造
            var param = new SpectrumMethodParams(originAList, dampingRatio, periodLength, deltaPeriod, integarteDt, duration);
            return param.GetResults(isPeseudo);
        }

        /// <summary>
        /// 获得反应谱分析结果
        /// </summary>
        /// <param name="originAList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="periodLength"></param>
        /// <param name="deltaPeriod"></param>
        /// <param name="integarteDt"></param>
        /// <param name="duration"></param>
        /// <returns></returns>
        public static List<SpectParam> GetResults(List<AccelParam> originAList, List<AccelParam> originYAList, 
            float dampingRatio = 0.05F, float periodLength = 10F,
            float deltaPeriod = 0.02F, float integarteDt = 0.02F, float duration = -1, bool isPeseudo = true)
        {
            //构造
            var param = new SpectrumMethodParams(originAList, originYAList, dampingRatio, periodLength, deltaPeriod, integarteDt, duration);
            return param.GetResults(isPeseudo);
        }

        /// <summary>
        /// 获得最大加速度 代方向
        /// </summary>
        /// <param name="accelList"></param>
        /// <returns></returns>
        public static float GetMaxAccel(List<AccelParam> accelList)
        {
            //列表为空直接返回
            if (accelList.Count == 0) return 1;
            //获得加速度列表
            var accelValues = (from accel in accelList select accel.Accel).ToList();
            //获得边界值
            var minValue = accelValues.Min();
            var maxValue = accelValues.Max();
            return Math.Abs(minValue) < Math.Abs(maxValue) ? maxValue : minValue;
        }

        /// <summary>
        /// 获得谱值
        /// </summary>
        /// <param name="spectList"></param>
        /// <param name="spectType"></param>
        /// <returns></returns>
        public static List<float> GetListFromSpectList(List<SpectParam> spectList, SpectrumType spectType)
        {
            return spectList.GetPropertyList(spectType.ToString()).ConvertAll(v => (float)v);
        }

        /// <summary>
        /// 获得圆频率
        /// </summary>
        /// <param name="period"></param>
        /// <returns></returns>
        public static float Getwn(float period)
        {
            return (float)(2 * Math.PI / period);
        }

        /// <summary>
        /// 获得圆频率
        /// </summary>
        /// <param name="period"></param>
        /// <returns></returns>
        public static float Getwd(float period, float DampRatio)
        {
            return Getwn(period) * (float)Math.Sqrt(1 - Math.Pow(DampRatio, 2));
        }
    }

    /// <summary>
    /// 反应谱求解参数
    /// </summary>
    public class SpectrumMethodParams
    {
        #region Variables

        /// <summary>
        /// 外部激励
        /// </summary>
        private AccelSet accels { set; get; }

        /// <summary>
        /// 外部激励
        /// </summary>
        private AccelSet accelsY { set; get; }

        /// <summary>
        /// 反应谱分析结果
        /// </summary>
        private SpectSet results { set; get; }

        /// <summary>
        /// NewMark参数
        /// </summary>
        private List<float> NewMarkParams { set; get; }

        /// <summary>
        /// Newmark参数
        /// </summary>
        private float Beta { set; get; }

        /// <summary>
        /// Newmark参数
        /// </summary>
        private float Gamma { set; get; }

        /// <summary>
        /// 阻尼比
        /// </summary>
        private float DampingRatio { set; get; }

        /// <summary>
        /// 周期长度
        /// </summary>
        private float PeriodLength { set; get; }

        /// <summary>
        /// 周期增量
        /// </summary>
        private float DeltaPeriod { set; get; }

        /// <summary>
        /// 积分时长
        /// </summary>
        private float Duration { set; get; }

        /// <summary>
        /// 积分步长
        /// </summary>
        private float IntegrateDT { set; get; }

        #endregion

        /// <summary>
        /// 清空Newmark参数
        /// </summary>
        public void ClearNewMark()
        {
            this.NewMarkParams.Clear();
        }

        /// <summary>
        /// 设定NewMark参数
        /// </summary>
        /// <param name="beta"></param>
        /// <param name="gamma"></param>
        public void SetNewMark(float beta, float gamma)
        {
            this.Beta = beta;
            this.Gamma = gamma;
            this.NewMarkParams = SpectrumMethod.GetNewMarkParamList(this.Beta, this.Gamma, this.IntegrateDT);
        }

        /// <summary>
        /// 求解反应谱时程
        /// </summary>
        /// <param name="isPeseudo"></param>
        /// <returns></returns>
        public List<SpectParam> GetResults(bool isPeseudo)
        {
            //确定积分时长
            this.Duration = this.Duration == -1 ? this.accels.OriginAccelMaxTime : this.Duration;
            //获得激励时程(带稀疏)
            var Accel = this.accels.GetSpraseAccelHist(this.IntegrateDT, this.Duration);
            //如果加速度为空则无需系数
            var AccelY = this.accelsY.Count == 0 ? new List<AccelParam>():
                this.accelsY.GetSpraseAccelHist(this.IntegrateDT, this.Duration);
            //初始化
            this.results = new SpectSet(Accel, AccelY, this.PeriodLength, this.DeltaPeriod);
            //返回分析结果
            return results.GetResults(this.NewMarkParams, this.DampingRatio, isPeseudo);
        }

        /// <summary>
        /// 求解反应谱
        /// </summary>
        /// <param name="isPeseudo"></param>
        /// <returns></returns>
        public SpectSet GetSpectParam(bool isPeseudo, int minTDtFactor)
        {
            //确定积分时长
            this.Duration = this.Duration == -1 ? this.accels.OriginAccelMaxTime : this.Duration;
            //获得激励时程(带稀疏)
            var Accel = this.accels.GetSpraseAccelHist(this.IntegrateDT, this.Duration);
            //初始化
            var spectrum =  new SpectSet(Accel, this.PeriodLength, this.DeltaPeriod, minTDtFactor);
            //判断Newmark参数是否存在
            if (this.NewMarkParams == null || this.NewMarkParams.Count == 0)
                return spectrum.GetSpectrumByExcitation(this.DampingRatio, this.IntegrateDT);
            //根据Newmark设定反应谱
            else return spectrum.GetSpectrumByNewmark(this.NewMarkParams, 
                this.DampingRatio, isPeseudo, this.IntegrateDT);
        }

        /// <summary>
        /// 求解反应谱
        /// </summary>
        /// <param name="isPseudo"></param>
        /// <returns></returns>
        public SpectSet GetSpectParamFast(bool isPseudo, List<double> periodList)
        {
            //获得激励时程
            var Accel = this.accels.OriginAccelList;
            //初始化
            var spectrum = new SpectSet(Accel, this.PeriodLength, this.DeltaPeriod, 1);
            //根据显式法计算结果
            return periodList.Count != 0? spectrum.GetSpectrumFast(periodList, this.DampingRatio, this.IntegrateDT):
                spectrum.GetSpectrumByExcitation(this.DampingRatio, this.IntegrateDT);
        }

        #region Construction

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public SpectrumMethodParams()
        {
            this.accels = new AccelSet();
            this.accelsY = new AccelSet();
            this.results = new SpectSet();
            this.NewMarkParams = new List<float>();
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="originAList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="periodLength"></param>
        /// <param name="deltaPeriod"></param>
        /// <param name="integarteDt"></param>
        /// <param name="duration"></param>
        public SpectrumMethodParams(List<AccelParam> originAList, float dampingRatio, float periodLength,
            float deltaPeriod, float integarteDt, float duration = -1)
        {
            this.accels = new AccelSet(originAList);
            this.accelsY = new AccelSet();
            this.DampingRatio = dampingRatio;
            this.PeriodLength = periodLength;
            this.DeltaPeriod = deltaPeriod;
            this.IntegrateDT = integarteDt;
            this.Duration = duration;
            //设定NewMark参数
            this.SetNewMark(0.25F, 0.5F);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="originAList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="periodLength"></param>
        /// <param name="deltaPeriod"></param>
        /// <param name="integarteDt"></param>
        /// <param name="duration"></param>
        public SpectrumMethodParams(List<AccelParam> originAList, List<AccelParam> originAYList, float dampingRatio, float periodLength,
            float deltaPeriod, float integarteDt, float duration = -1)
        {
            this.accels = new AccelSet(originAList);
            this.accelsY = new AccelSet(originAYList);
            this.DampingRatio = dampingRatio;
            this.PeriodLength = periodLength;
            this.DeltaPeriod = deltaPeriod;
            this.IntegrateDT = integarteDt;
            this.Duration = duration;
            //设定NewMark参数
            this.SetNewMark(0.25F, 0.5F);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="accelSet"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="periodLength"></param>
        /// <param name="deltaPeriod"></param>
        /// <param name="integarteDt"></param>
        public SpectrumMethodParams(AccelSet accelSet, float dampingRatio, 
            float periodLength, float deltaPeriod, float integarteDt)
        {
            this.accels = accelSet;
            this.accelsY = new AccelSet();
            this.DampingRatio = dampingRatio;
            this.PeriodLength = periodLength;
            this.DeltaPeriod = deltaPeriod;
            this.IntegrateDT = integarteDt;
            this.Duration = -1;
            //设定NewMark参数
            this.SetNewMark(0.25F, 0.5F);
        }

        #endregion
    }
}
